///*
// * Timer.cpp
// *
// *  Created on: 6-mrt-2012
// *      Author: Bart
// */
//
//#include "Timer.h"
//
////TODO verwijdere
///*
//XTmrCtr* TmrCtrInstancePtr;
//TmrCtrInstancePtr = &TimerCounterInst;
//*/
//extern "C" void TimerCounterHandler(void *CallBackRef, Xuint8 TmrCtrNumber);
//
//int Timer::initialize(){
//      int Status;
//
//      /*
//       * Initialize the timer counter so that it's ready to use.
//       */
//      Status = XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
//      if (Status != XST_SUCCESS) {
//              if(Status != XST_DEVICE_IS_STARTED){
//                      return XST_FAILURE;
//              }
//      }
//
//      /*
//       * Perform a self-test to ensure that the hardware was built correctly.
//       */
//      Status = XTmrCtr_SelfTest(&TimerCounterInst, TMRCTR_DEVICE_ID);
//      if (Status != XST_SUCCESS){
//              return XST_FAILURE;
//      }
//
//      /*
//       *  Connect the timer counter to the interrupt subsystem such that interrupts can occur.
//       */
//      Status = setupIntrSystem(&InterruptController,
//                                                      &TimerCounterInst,
//                                                      TMRCTR_DEVICE_ID,
//                                                      TMRCTR_INTERRUPT_ID,
//                                                      TIMER_CNTR_0);
//      if (Status != XST_SUCCESS) {
//              return XST_FAILURE;
//      }
//
//      /*
//       * Set interrupthandler
//       */
//      XTmrCtr_SetHandler(&TimerCounterInst, TimerCounterHandler, &TimerCounterInst);
//
//      /*
//       * Enable the interrupt of the timer counter so interrupts will occur
//       * and use auto reload mode such that the timer counter will reload
//       * itself automatically and continue repeatedly, without this option
//       * it would expire once only
//       */
//      XTmrCtr_SetOptions(&TimerCounterInst, TIMER_CNTR_0,
//                              XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION);
//
//      /*
//       * Set a reset value for the timer counter such that it will expire
//       * eariler than letting it roll over from 0, the reset value is loaded
//       * into the timer counter when it is started
//       */
//      XTmrCtr_SetResetValue(&TimerCounterInst, TIMER_CNTR_0, RESET_VALUE);
//
//      /*
//       * Start the timer counter such that it's incrementing by default,
//       * then wait for it to timeout a number of times
//       */
//      XTmrCtr_Start(&TimerCounterInst, TIMER_CNTR_0);
//
//      return XST_SUCCESS;
//
//}
//
//int Timer::setupIntrSystem(XIntc* IntcInstancePtr,
//                                      XTmrCtr* TmrCtrInstancePtr,
//                                      Xuint16 DeviceId,
//                                      Xuint16 IntrId,
//                                      Xuint8 TmrCtrNumber){
//      int Status;
//
//      /*
//       * Initialize the interrupt controller driver so that
//       * it's ready to use, specify the device ID that is generated in
//       * xparameters.h
//       */
//      Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
//      if (Status != XST_SUCCESS) {
//              return XST_FAILURE;
//      }
//
//      /*
//       * Connect a device driver handler that will be called when an interrupt
//       * for the device occurs, the device driver handler performs the specific
//       * interrupt processing for the device
//       */
//      Status = XIntc_Connect(IntcInstancePtr, IntrId,
//                              (XInterruptHandler)XTmrCtr_InterruptHandler,
//                              (void *)TmrCtrInstancePtr);
//      if (Status != XST_SUCCESS) {
//              return XST_FAILURE;
//      }
//
//      /*
//       * Start the interrupt controller such that interrupts are enabled for
//       * all devices that cause interrupts, specific real mode so that
//       * the timer counter can cause interrupts thru the interrupt controller.
//       */
//      Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
//      if (Status != XST_SUCCESS) {
//              return XST_FAILURE;
//      }
//
//      /*
//       * Enable the interrupt for the timer counter
//       */
//      XIntc_Enable(IntcInstancePtr, IntrId);
//
//      /*
//       * Initialize the exception table.
//       */
//      Xil_ExceptionInit();
//
//      /*
//       * Register the interrupt controller handler with the exception table.
//       */
//      Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
//                                      (Xil_ExceptionHandler)
//                                      XIntc_InterruptHandler,
//                                      IntcInstancePtr);
//
//      /*
//       * Enable non-critical exceptions.
//       */
//      Xil_ExceptionEnable();
//
//      return XST_SUCCESS;
//}
//
//extern "C" void TimerCounterHandler(void *CallBackRef, Xuint8 TmrCtrNumber)
//{
//      /*
//       * Hier komt eigen code
//       *
//       *
//       */
//      XTmrCtr *InstancePtr = (XTmrCtr *)CallBackRef;
//
//      /*
//       * Check if the timer counter has expired, checking is not necessary
//       * since that's the reason this function is executed, this just shows
//       * how the callback reference can be used as a pointer to the instance
//       * of the timer counter that expired, increment a shared variable so
//       * the main thread of execution can see the timer expired
//       *
//      if (XTmrCtr_IsExpired(InstancePtr, TmrCtrNumber)) {
//              TimerExpired++;
//              if(TimerExpired == 3) {
//                      XTmrCtr_SetOptions(InstancePtr, TmrCtrNumber, 0);
//              }
//      }
//      */
//}

/*
 * Timer.c
 *
 *  Created on: 18-mrt-2012
 *      Author: Bart
 */

#include "Timer.h"

/************************** Variable Definitions *****************************/
/*
 * Volatile?
 * Indien de hardware deze variabalen kan veranderen.
 */
volatile Xuint32 timer0_overflow_count = 0;
volatile Xuint32 timer0_millis = 0;
static unsigned char timer0_fract = 0;

XTmrCtr TimerCounterInst;
XIntc InterruptContr;

volatile int TimerExpired;

/************************** Function Prototypes ******************************/
//TODO best een algemene setupInterrupt van maken. -> In aparte C file
int setupIntrSystem(XIntc* IntcInstancePtr,
                                        XTmrCtr* TmrCtrInstancePtr,
                                        Xuint16 DeviceId,
                                        Xuint16 IntrId,
                                        Xuint8 TmrCtrNumber);

void TimerCounterHandler(void *CallBackRef, u8 TmrCtrNumber);

void TmrCtrDisableIntr(XIntc* IntcInstancePtr, u16 IntrId);



/*****************************************************************************/
/*
 *      Functions
 *
 *****************************************************************************/
int initialize(void){
        int Status;

        /*
         * Initialize the timer counter so that it's ready to use.
         */
        Status = XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
        if (Status != XST_SUCCESS) {
                if(Status != XST_DEVICE_IS_STARTED){
                        return XST_FAILURE;
                }
        }

        /*
         * Perform a self-test to ensure that the hardware was built correctly.
         */
        Status = XTmrCtr_SelfTest(&TimerCounterInst, TMRCTR_DEVICE_ID);
        if (Status != XST_SUCCESS){
                return XST_FAILURE;
        }

        /*
         *  Connect the timer counter to the interrupt subsystem such that interrupts can occur.
         */
        Status = setupIntrSystem(&InterruptContr,
                                                        &TimerCounterInst,
                                                        TMRCTR_DEVICE_ID,
                                                        INTC_DEVICE_ID,
                                                        TIMER_CNTR_0);
        if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }

        /*
         * Set interrupthandler
         */
        XTmrCtr_SetHandler(&TimerCounterInst, TimerCounterHandler, &TimerCounterInst);

        /*
         * Enable the interrupt of the timer counter so interrupts will occur
         * and use auto reload mode such that the timer counter will reload
         * itself automatically and continue repeatedly, without this option
         * it would expire once only
         */
        XTmrCtr_SetOptions(&TimerCounterInst, TIMER_CNTR_0,
                                XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION);

        /*
         * Set a reset value for the timer counter such that it will expire
         * eariler than letting it roll over from 0, the reset value is loaded
         * into the timer counter when it is started
         */
        XTmrCtr_SetResetValue(&TimerCounterInst, TIMER_CNTR_0, (int)RESET_VALUE);

        /*
         * Start the timer counter such that it's incrementing by default,
         * then wait for it to timeout a number of times
         */
        XTmrCtr_Start(&TimerCounterInst, TIMER_CNTR_0);

        return XST_SUCCESS;
}

int setupIntrSystem(XIntc* IntcInstancePtr,
                                        XTmrCtr* TmrCtrInstancePtr,
                                        Xuint16 DeviceId,
                                        Xuint16 IntrId,
                                        Xuint8 TmrCtrNumber){
        int Status;

        /*
         * Initialize the interrupt controller driver so that
         * it's ready to use, specify the device ID that is generated in
         * xparameters.h
         */
        Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
        if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }

        /*
         * Connect a device driver handler that will be called when an interrupt
         * for the device occurs, the device driver handler performs the specific
         * interrupt processing for the device
         */
        Status = XIntc_Connect(IntcInstancePtr, IntrId,
                                (XInterruptHandler)XTmrCtr_InterruptHandler,
                                (void *)TmrCtrInstancePtr);
        if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }

        /*
         * Start the interrupt controller such that interrupts are enabled for
         * all devices that cause interrupts, specific real mode so that
         * the timer counter can cause interrupts thru the interrupt controller.
         */
        Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
        if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }

        /*
         * Enable the interrupt for the timer counter
         */
        XIntc_Enable(IntcInstancePtr, IntrId);

        /*
         * Initialize the exception table.
         */
        Xil_ExceptionInit();

        /*
         * Register the interrupt controller handler with the exception table.
         */
        Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                        (Xil_ExceptionHandler)
                                        XIntc_InterruptHandler,
                                        IntcInstancePtr);

        /*
         * Enable non-critical exceptions.
         */
        Xil_ExceptionEnable();

        return XST_SUCCESS;
}

void TimerCounterHandler(void *CallBackRef, Xuint8 TmrCtrNumber){
        XTmrCtr *InstancePtr = (XTmrCtr *)CallBackRef;

        //TODO testen
        if (XTmrCtr_IsExpired(InstancePtr, TmrCtrNumber)){
                //Increment millis, overflow count

                timer0_millis += TIMING_INTERVAL;       // Check if TIMING_INTERVAL is in ms.
                timer0_overflow_count ++;

        }
}

Xuint32 millis(){
        Xuint32 m;

        //disable interrupt, otherwise we will get inconsistent values for millis.
        XIntc_Disable(&InterruptContr, INTC_DEVICE_ID);
        m = timer0_millis;
        XIntc_Enable(&InterruptContr, INTC_DEVICE_ID);
        return m;
}

Xuint32 micros(){
        Xuint32 m;
        Xuint32 micros;

        micros = timer0_overflow_count * TIMING_INTERVAL * 1000;

        Xuint32 timerValue = XTmrCtr_GetValue(&TimerCounterInst, TMRCTR_DEVICE_ID);
        m = (RESET_VALUE - timerValue) * TIMING_INTERVAL * 1000 / RESET_VALUE;

        micros = micros + m;
        return micros;
}

void delay(Xuint32 timedelay){
        Xuint32 start = millis();

        while (timedelay > 0) {
                if ((millis() - start) >= 1) {
                        timedelay--;
                        start += 1;
                }
        }
}
